package com.huadao.cart_ccs.data.source.serialport;

import android.util.Log;

import com.cart.event.SerialPortEvent;
import com.cart.utils.FormatUtils;
import com.daolion.base.utils.StringUtils;
import com.huadao.cart_ccs.Event.ConsoleEvent;
import com.huadao.cart_ccs.base.MainDataManager;
import com.huadao.cart_ccs.common.ByteUtils;
import com.huadao.cart_ccs.common.ExperimentStageManager;
import com.huadao.cart_ccs.common.ProjectManager;
import com.huadao.cart_ccs.common.UtilHelpers;
import com.huadao.cart_ccs.common.threadControl.ThreadPoolProxyFactory;
import com.huadao.cart_ccs.data.bean.SerialPortInfo;
import com.huadao.cart_ccs.data.bo.Protocol;
import com.huadao.cart_ccs.exception.ExceptionCode;
import com.huadao.cart_ccs.exception.ExceptionType;
import com.huadao.cart_ccs.greendao.entity.ExceptionInfo;
import com.huadao.cart_ccs.main.CartApplication;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.greendao.async.AsyncSession;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import java.util.Date;

import serialport.SerialPort;

public class SerialPortSource {

    private static SerialPortSource serialPortSource = new SerialPortSource();
    private static SerialPort mSerialPort = null;
    private static OutputStream mOutputStream;
    private static InputStream mInputStream;
    private static final String TAG = "SerialPortSource";

    //两次发送的最小间隔时间
    public static long minSendTimeGap = 50;
    //最近一次发送时间
    private static long lastSendTime;
    //发送串口成功和失败
    public static final int SendSucc = 1;
    public static final int SendFailed = 0;

    private static AsyncSession asyncSession = MainDataManager.getInstance().getAsyncSession();

    private static SerialPortEvent serialPortEvent = new SerialPortEvent();

    public static SerialPortSource instance() {
        if (mSerialPort == null) {
            synchronized (SerialPortSource.class) {
                try {
                    mSerialPort = openSerialPort();
                    if (mSerialPort != null) {
                        mOutputStream = mSerialPort.getOutputStream();
                        mInputStream = mSerialPort.getInputStream();
                        serialPortEvent.setIsConnected(true);
                        EventBus.getDefault().post(serialPortEvent);
                    } else {
                        serialPortEvent.setIsConnected(false);
                        EventBus.getDefault().post(serialPortEvent);
                    }
                } catch (InvalidParameterException e) {
                    e.printStackTrace();
                    saveException("串口打开失败:参数错误");
                    serialPortEvent.setIsConnected(false);
                    EventBus.getDefault().post(serialPortEvent);
                }
            }
        }
        return serialPortSource;
    }

    public InputStream getInputStream() {
        return mInputStream;
    }

    private static void saveException(String message) {
        long projectId = ProjectManager.getInstance().getProjectId();
        long stageId = ExperimentStageManager.getInstance().getStageId();
        asyncSession
                .insert(new ExceptionInfo(null, new Date(), projectId, stageId, ExceptionCode.SERIALPORT, message, ExceptionType.ERROR_SERIALPORT));
    }

    private static SerialPort openSerialPort() {
        SerialPort mSerialPort = null;
        Log.i(TAG, "正在打开串口");
        SerialPortInfo serialPortInfo = MainDataManager.getInstance().getSerialPortInfo();
        if (serialPortInfo == null || StringUtils.isEmpty(serialPortInfo.getDevice()) || serialPortInfo.getBaudrate() < 1) {
            Log.e(TAG, "串口打开失败:参数错误");
            throw new InvalidParameterException("串口打开失败:参数错误");
        }
        try {
            mSerialPort = new SerialPort(new File(serialPortInfo.getDevice()), serialPortInfo.getBaudrate(), 0);
        } catch (SecurityException e) {
            e.printStackTrace();
            Log.e(TAG, "串口打开失败:SecurityException");
            saveException("串口打开失败:SecurityException");
        } catch (IOException e) {
            e.printStackTrace();
            saveException("串口打开失败:native open returns null");
        }
        if (mSerialPort != null) {
            Log.i("串口", "串口打开成功");
        }
        return mSerialPort;
    }

    public void closeSerialPort() {
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
            Log.i("串口", "关闭串口");
        }
    }

    private void sendMsg(byte[] msg) {
        if (mOutputStream != null) {
            try {
                mOutputStream.write(msg);
                mOutputStream.flush();
                EventBus.getDefault().post(new ConsoleEvent(ByteUtils
                        .getHexByte(msg, 0, msg.length), ConsoleEvent.TYPE_REQUEST, new Date()));
                Log.i(TAG, "REQUEST " + ByteUtils.getHexByte(msg, 0, msg.length));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            return;
        }
    }

    public void serialPortRequest(final byte[] msg) {
        if (!UtilHelpers.isServiceRunning(CartApplication.getInstance(), "com.huadao.cart_ccs.main.CommandService")) {
            serialPortEvent.setIsConnected(false);
            EventBus.getDefault().post(serialPortEvent);
            return;
        }
        ThreadPoolProxyFactory.getSerialPortThreadPoolProxy().execute(() -> {
            long now = System.currentTimeMillis();
            long gap = now - lastSendTime;
            if (gap < minSendTimeGap) {
                try {
                    Thread.sleep(minSendTimeGap - gap);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lastSendTime = System.currentTimeMillis();
            sendMsg(msg);
        });
    }

    public int serialPortRequest(String requestData) {
        if (!UtilHelpers.isServiceRunning(CartApplication.getInstance(), "com.huadao.cart_ccs.main.CommandService")) {
            serialPortEvent.setIsConnected(false);
            EventBus.getDefault().post(serialPortEvent);
            return SendFailed;
        }
        ThreadPoolProxyFactory.getSerialPortThreadPoolProxy().execute(() -> {
            long now = System.currentTimeMillis();
            long gap = now - lastSendTime;
            if (gap < minSendTimeGap) {
                try {
                    Thread.sleep(minSendTimeGap - gap);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lastSendTime = System.currentTimeMillis();
            sendMsg(requestData);
        });
        return SendSucc;
    }

    public int serialPortRequest(final Protocol protocol) {
        if (!UtilHelpers.isServiceRunning(CartApplication.getInstance(), "com.huadao.cart_ccs.main.CommandService")) {
            serialPortEvent.setIsConnected(false);
            EventBus.getDefault().post(serialPortEvent);
            return SendFailed;
        }
        ThreadPoolProxyFactory.getSerialPortThreadPoolProxy().execute(() -> {
            long now = System.currentTimeMillis();
            long gap = now - lastSendTime;
            if (gap < minSendTimeGap) {
                try {
                    Thread.sleep(minSendTimeGap - gap);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lastSendTime = System.currentTimeMillis();
            sendMsg(protocol);
        });
        return SendSucc;
    }

    private void sendMsg(Protocol protocol) {
        if (mOutputStream != null) {
            try {
                byte[] msg = protocol.toByteArray();
                mOutputStream.write(msg);
                mOutputStream.flush();
                String request = ByteUtils.getHexByte(msg, 0, msg.length);

                EventBus.getDefault().post(new ConsoleEvent(request, ConsoleEvent.TYPE_REQUEST, new Date()));
                protocol.setDate(new Date());
                asyncSession.insert(protocol);
                Log.i(TAG, "REQUEST " + request);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            return;
        }
    }

    private void sendMsg(String command) {
        if (mOutputStream == null) {
            return;
        }
        try {
            Log.i("CardFunctionImpl", FormatUtils.formatterDataToShow(command));
            final byte[] bytes = FormatUtils.toByteArray(command);
            mOutputStream.write(bytes, 0, bytes.length);
            mOutputStream.flush();
            EventBus.getDefault().post(new ConsoleEvent(command, ConsoleEvent.TYPE_REQUEST, new Date()));
            Log.i(TAG, "REQUEST " + command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
